---
title: Search as we type
---

:::caution
The content of this page may be outdated.  
It will be updated in the future, but for now you may want to refer to the content
in the top of the sidebar instead (introduction/essentials/case-studies/...)
:::

A real world example could be to use `FutureProvider` to implement a searchbar.

## Usage example: "Search as we type" searchbar

Implementing a "search as we type" can seem daunting at first when using
conventional means.  
There are lots of moving parts, such as:

- handling errors.
- debouncing the user input to avoid making network requests on every keystroke.
- cancelling previously pending network requests when the search field changes.

But the combination of `FutureProvider` and the power of [ref.watch] can
significantly simplify this task.

A common pattern wanting to perform an asynchronous requests
is to split it into multiple providers:

- a [StateNotifierProvider] or `StateProvider` for the parameters of your request
  (or alternatively use [family])
- a `FutureProvider`, which will do the request by reading the parameters from
  the other providers/[family].

The first step would be to store the user input somewhere. For this example,
we will use `StateProvider` (as the search state is only a single `String`):

```dart
final searchInputProvider = StateProvider<String>((ref) => '');
```

We can then connect this provider to a [TextField] by doing:

```dart
Consumer(
  builder: (context, ref, child) {
    return TextField(
      onChanged: (value) => ref.read(searchInputProvider.notifier).state = value,
    );
  },
)
```

Then, we can create our `FutureProvider` which will take care of the request:

```dart
final searchProvider = FutureProvider<


<!--
// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

final searchFieldProvider = StateProvider<String>((ref) => '');
final questionsProvider = FutureProvider<List>((ref) async {
  final client = http.Client();
  ref.onDispose(client.close);

  final search = ref.watch(searchFieldProvider);

  Uri uri;

  if (search.isEmpty) {
    uri = Uri.parse(
      'https://api.stackexchange.com/2.3/questions?order=desc&sort=activity&site=stackoverflow',
    );
  } else {
    final encodedQuery = Uri.encodeComponent(search);
    uri = Uri.parse(
      'https://api.stackexchange.com/2.3/search?order=desc&sort=activity&intitle=$encodedQuery&site=stackoverflow',
    );
  }

  final response = await client.get(uri);
  final questions = jsonDecode(response.body);

  return questions['items'].map((question) => question['title']).toList();
});

void main() => runApp(const ProviderScope(child: MyApp()));

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyHomePage());
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final questions = ref.watch(questionsProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('Questions')),
      body: Column(
        children: [
          TextField(
            onChanged: (value) =>
                ref.read(searchFieldProvider.notifier).state = value,
          ),
          Expanded(
            child: switch (questions) {
              AsyncData(:final value) => ListView.builder(
                  itemCount: value.length,
                  itemBuilder: (context, index) {
                    final question = value[index];

                    return ListTile(
                      title: Text(
                        question.toString(),
                      ),
                    );
                  },
                );,
              AsyncError(:final error) => Center(child: Text('Error $error')),
              _ => const Center(child: CircularProgressIndicator()),
            },
          ),
        ],
      ),
    );
  }
}
```
